﻿/**
 * 剖分可行走区域为多个相连三角形，
 */
 
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

public class Delaunay : Editor
{
    //所有的点
    static private List<Vector3> mPoints = new List<Vector3>();

    //约束线
    static private List<Line> mConstraintLines = new List<Line>();

    //启发线
    static private List<Line> mEnlightenLines = new List<Line>();

    static public List<Triangle> mTriangleList = new List<Triangle>();

    [MenuItem("寻路/创建寻路网格")]
    static void CreateGuideMesh()
    {
        Caching.CleanCache();

        mPoints.Clear();
        mConstraintLines.Clear();
        mEnlightenLines.Clear();
        mTriangleList.Clear();

        CreateConstraintLine();

        if (mPoints.Count >= 2)
        {
            mEnlightenLines.Add(new Line(mPoints[0], mPoints[1]));
            DelaunayFunc();
        }
    }

    [MenuItem("寻路/创建寻路网格2")]
    static void CreateGuideMesh2()
    {
        Caching.CleanCache();

        mTriangleList.Clear();

        string assetPath = "Assets/RecastNav/obj/" + SceneManager.GetActiveScene().name + ".obj";
        Mesh mesh = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Mesh)) as Mesh;
        Vector3[] vertexs = mesh.vertices;
        for (int i = 0; i < mesh.subMeshCount; i++)
        {
            int[] indices = mesh.GetIndices(i);
            for(int j = 0; j < indices.Length; j += 3)
            {
                Vector3 p1 = vertexs[indices[j]];
                Vector3 p2 = vertexs[indices[j+1]];
                Vector3 p3 = vertexs[indices[j+2]];
                p1.x = -p1.x;
                p2.x = -p2.x;
                p3.x = -p3.x;
                Triangle triangle = new Triangle(p1, p2, p3);
                mTriangleList.Add(triangle);
            }
        }

        for (int i = 0; i < mTriangleList.Count; i++)
        {
            Triangle tempTriangle = mTriangleList[i];
            tempTriangle.Index = i;
        }

        //设置三角形之间的关系
        for (int i = 0; i < mTriangleList.Count - 1; i++)
        {
            for (int j = i + 1; j < mTriangleList.Count; j++)
            {
                Triangle triangle1 = mTriangleList[i];
                Triangle triangle2 = mTriangleList[j];
                ConnectTriangle(triangle1, triangle2);
            }
        }

        GuideMeshData data = ScriptableObject.CreateInstance<GuideMeshData>();
        data.mTriangleList = mTriangleList;
        string path = "Assets/Config/" + SceneManager.GetActiveScene().name + ".asset";
        AssetDatabase.CreateAsset(data, path);
        AssetDatabase.Refresh();
    }

    /// <summary>
    /// 合并不可行走区域
    /// </summary>
    static List<List<Vector3>> MergerNowalkArea()
    {
        GameObject[] nowalks = GameObject.FindGameObjectsWithTag("nowalk");
        List<List<Vector3>> mColliderList = new List<List<Vector3>>();
        for (int i = 0; i < nowalks.Length; i++)
        {
            PolygonCollider collider = nowalks[i].GetComponent<PolygonCollider>();
            if (collider != null)
                mColliderList.Add(collider.mBounds);
        }
        bool merger = true;
        while (merger)
        {
            merger = false;
            for (int i = 0; i < mColliderList.Count - 1; i++)
            {
                for (int j = i + 1; j < mColliderList.Count; j++)
                {
                    List<List<Vector3>> temp = MergerPolygon.Union(mColliderList[i], mColliderList[j]);
                    if (temp != null)
                    {
                        mColliderList.RemoveAt(j);
                        mColliderList.RemoveAt(i);
                        mColliderList.AddRange(temp);
                        merger = true;
                        break;
                    }
                }
                if (merger)
                    break;
            }
        }
        return mColliderList;
    }

    /// <summary>
    /// 添加定点和生成约束线
    /// </summary>
    static void CreateConstraintLine()
    {
        GameObject floor = GameObject.FindGameObjectWithTag("walk");
        PolygonCollider collider = floor.GetComponent<PolygonCollider>();
        if (collider != null)
        {
            for (int j = 0; j < collider.mBounds.Count; j++)
            {
                Vector3 vec = collider.mBounds[j];
                mPoints.Add(vec);
                mConstraintLines.Add(new Line(vec, collider.mBounds[(j + 1) % collider.mBounds.Count]));
            }
        }

        List<List<Vector3>> mColliderList = MergerNowalkArea();
        for (int i = 0; i < mColliderList.Count; i++)
        {
            List<Vector3> mBounds = mColliderList[i];
            for (int j = 0; j < mBounds.Count; j++)
            {
                Vector3 vec = mBounds[j];
                mPoints.Add(vec);
                mConstraintLines.Add(new Line(vec, mBounds[(j + 1) % mBounds.Count]));
            }
        }
    }

    /// <summary>
    /// 生成三角形核心方法
    /// </summary>
    static void DelaunayFunc()
    {
        List<Vector3> veclist = new List<Vector3>();
        List<Line> obstaclelines = new List<Line>();

        while (mEnlightenLines.Count > 0)
        {
            veclist.Clear();
            obstaclelines.Clear();

            //获取启发线
            Line line = mEnlightenLines[0];

            //获取所有可见点
            for (int i = 0; i < mPoints.Count; i++)
            {
                Vector3 curPoint = mPoints[i];
                // 判断点是否在线的右边（注意unity坐标系为左手坐标系）
                if (Vector3.Cross(curPoint - line.mPoint1, line.mPoint2 - line.mPoint1).y < 0)
                {
                    bool enabled = true;

                    Line line1 = new Line(line.mPoint1, curPoint);
                    Line line2 = new Line(line.mPoint2, curPoint);

                    //判断点是否是启发线两点的可见点
                    //可见点：（点a与点b的连线不与任何约束线和新生成的三角形相交，ab互为对方可见点）
                    for (int j = 0; j < mConstraintLines.Count; j++)
                    {
                        obstaclelines.Add(mConstraintLines[j]);
                    }
                    for (int m = 0; m < mTriangleList.Count; m++)
                    {
                        Triangle triangle = mTriangleList[m];
                        for (int j = 0; j < triangle.mLines.Length; j++)
                        {
                            obstaclelines.Add(triangle.mLines[j]);
                        }
                    }
                    for (int j = 0; j < obstaclelines.Count; j++)
                    {
                        Line constraintLine = obstaclelines[j];
                        //注意排除顶点
                        if (constraintLine.ContainPoint(curPoint))
                            continue;
                        if (Line.IsIntersect(line1, constraintLine) && !constraintLine.ContainPoint(line1.mPoint1))
                        {
                            enabled = false;
                            break;
                        }
                        if (Line.IsIntersect(line2, constraintLine) && !constraintLine.ContainPoint(line2.mPoint1))
                        {
                            enabled = false;
                            break;
                        }
                    }
                    if (enabled)
                        veclist.Add(curPoint);
                }
            }

            mEnlightenLines.RemoveAt(0);

            if (veclist.Count > 0)
            {
                Vector3 mDT = Vector3.zero;
                float angle = -360;
                //获取DT点
                for (int i = 0; i < veclist.Count; i++)
                {
                    Vector3 dir1 = line.mPoint1 - veclist[i];
                    Vector3 dir2 = line.mPoint2 - veclist[i];

                    float temp_angle = Vector3.Angle(dir1, dir2);

                    if (temp_angle > angle)
                    {
                        mDT = veclist[i];
                        angle = temp_angle;
                    }
                }
                if (angle != -360)
                {
                    Triangle triangle = new Triangle(line.mPoint1, line.mPoint2, mDT);
                    mTriangleList.Add(triangle);

                    //新生成的启发线
                    List<Line> newLines = new List<Line>()
                    {
                        new Line(line.mPoint1, mDT),
                        new Line(mDT, line.mPoint2)
                    };

                    for (int i = 0; i < newLines.Count; i++)
                    {
                        Line newLine = newLines[i];
                        bool isConstraint = false;
                        for (int j = 0; j < mConstraintLines.Count; j++)
                        {
                            Line constraintLine = mConstraintLines[j];
                            if (constraintLine.mPoint1 == newLine.mPoint1 && constraintLine.mPoint2 == newLine.mPoint2)
                                isConstraint = true;
                        }
                        //如果新的启发线非约束线
                        if (!isConstraint)
                        {
                            int index = mEnlightenLines.FindIndex(o => (o.mPoint1 == newLine.mPoint2 && o.mPoint2 == newLine.mPoint1));
                            //启发线列表中不包含新的启发线，添加
                            if (index == -1)
                                mEnlightenLines.Add(newLine);
                            //启发线列表中包含新的启发线，移除列表中的该启发线
                            else
                                mEnlightenLines.RemoveAt(index);
                        }
                    }


                }
            }
        }

        for (int i = 0; i < mTriangleList.Count; i++)
        {
            Triangle tempTriangle = mTriangleList[i];
            tempTriangle.Index = i;
        }

        //设置三角形之间的关系
        for (int i = 0; i < mTriangleList.Count - 1; i++)
        {
            for (int j = i + 1; j < mTriangleList.Count; j++)
            {
                Triangle triangle1 = mTriangleList[i];
                Triangle triangle2 = mTriangleList[j];
                ConnectTriangle(triangle1, triangle2);
            }
        }

        GuideMeshData data = ScriptableObject.CreateInstance<GuideMeshData>();
        data.mTriangleList = mTriangleList;
        string path = "Assets/Config/" + SceneManager.GetActiveScene().name + ".asset";
        AssetDatabase.CreateAsset(data, path);
        AssetDatabase.Refresh();
    }

    /// <summary>
    /// 连接相连三角形
    /// </summary>
    /// <param name="tTriangle1"></param>
    /// <param name="tTriangle2"></param>
    static void ConnectTriangle(Triangle tTriangle1, Triangle tTriangle2)
    {
        for (int i = 0; i < tTriangle1.mLines.Length; i++)
        {
            Line line1 = tTriangle1.mLines[i];
            for (int j = 0; j < tTriangle2.mLines.Length; j++)
            {
                Line line2 = tTriangle2.mLines[j];
                if (line1.mPoint1 == line2.mPoint2 && line1.mPoint2 == line2.mPoint1)
                {
                    line1.mLeftTriangle = tTriangle2.Index;
                    line2.mLeftTriangle = tTriangle1.Index;
                }
            }
        }
    }
}
